#include "modbusrtu.h"
#include <QDateTime>
#include <QMessageBox>

ModbusRTU::ModbusRTU(SerialManager *serialManager, QObject *parent)
    : QObject(parent)
    , serialManager_(serialManager)
    , timeoutTimer_(new QTimer(this))
{
    connect(serialManager_, &SerialManager::dataReady, this, &ModbusRTU::onDataReady);
    connect(timeoutTimer_, &QTimer::timeout, this, &ModbusRTU::handleTimeout);
}

void ModbusRTU::readData(const ModbusParams &params)
{
    if (!serialManager_->isOpen()) {
        emit errorOccurred("设备未连接");
        return;
    }

    currentParams_ = params;
    QByteArray request = buildModbusRequest(params);
    if (request.isEmpty()) {
        emit errorOccurred("构建请求失败");
        return;
    }

    sendRequest(request);
}

void ModbusRTU::writeData(const ModbusParams &params)
{
    if (!serialManager_->isOpen()) {
        emit errorOccurred("设备未连接");
        return;
    }

    currentParams_ = params;
    QByteArray request = buildModbusRequest(params);
    if (request.isEmpty()) {
        emit errorOccurred("构建请求失败");
        return;
    }

    sendRequest(request);
}

void ModbusRTU::sendRequest(const QByteArray &request)
{
    lastRequestFrame_ = request;
    currentRetryCount_ = 0;
    waitingResponse_ = true;
    responseBuffer_.clear();

    qint64 bytesWritten = serialManager_->writeData(request);
    if (bytesWritten == -1) {
        emit errorOccurred("发送数据失败: " + serialManager_->lastError());
        waitingResponse_ = false;
        return;
    }

    startTimeoutMonitoring(currentParams_.timeout);

    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
    QString log = QString("[%1] 发送请求帧: %2")
                      .arg(timestamp)
                      .arg(request.toHex(' ').toUpper());
    emit responseReady(log);
}

QByteArray ModbusRTU::calculateCrc(const QByteArray &data)
{
    unsigned int crc = 0xFFFF;
    for (int i = 0; i < data.size(); ++i) {
        crc ^= static_cast<unsigned char>(data[i]);
        for (int j = 0; j < 8; ++j) {
            if (crc & 0x0001)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc >>= 1;
        }
    }
    QByteArray result;
    result.append(static_cast<char>(crc & 0xFF));
    result.append(static_cast<char>(crc >> 8));
    return result;
}

QByteArray ModbusRTU::buildModbusRequest(const ModbusParams &params)
{
    int slaveAddr = params.slaveAddr;
    int startAddr = params.startAddr;
    quint16 count = params.count;
    expectedCoilCount_ = count;

    QString funcCode;
    if (params.function == "(0x01)读线圈")
        funcCode = "01";
    else if (params.function == "(0x03)读寄存器")
        funcCode = "03";
    else if (params.function == "(0x0F)写线圈")
        funcCode = "0F";
    else if (params.function == "(0x10)写寄存器")
        funcCode = "10";
    else {
        emit errorOccurred("请选择正确的功能码");
        return QByteArray();
    }

    if ((funcCode == "0F" || funcCode == "10") && params.data.isEmpty()) {
        emit errorOccurred("请输入写入数据");
        return QByteArray();
    }

    QString slaveStrAddr = QString("%1").arg(slaveAddr, 2, 16, QChar('0')).toUpper();
    QString addrHex = QString("%1").arg(startAddr, 4, 16, QChar('0')).toUpper();
    QString countHex = QString("%1").arg(count, 4, 16, QChar('0')).toUpper();
    QString message = slaveStrAddr + funcCode + addrHex + countHex;

    if (funcCode == "0F") {
        if (params.data.length() != count) {
            emit errorOccurred("二进制数据长度不匹配");
            return QByteArray();
        }
        int byteCount = (count + 7) / 8;
        QString byteCountHex = QString("%1").arg(byteCount, 2, 16, QChar('0')).toUpper();
        QString dataHex;
        for (int i = 0; i < byteCount; ++i) {
            quint8 byte = 0;
            for (int bit = 0; bit < 8; ++bit) {
                int dataIndex = i * 8 + bit;
                if (dataIndex < params.data.length() && params.data[dataIndex] == '1')
                    byte |= (1 << bit);
            }
            dataHex += QString("%1").arg(byte, 2, 16, QChar('0')).toUpper();
        }
        message += (byteCountHex + dataHex);
    } else if (funcCode == "10") {
        QStringList dataList = params.data.split(',');
        if (dataList.size() != count) {
            emit errorOccurred("数据数量不匹配");
            return QByteArray();
        }
        int byteCount = count * 2;
        QString byteCountHex = QString("%1").arg(byteCount, 2, 16, QChar('0')).toUpper();
        QString dataHex;
        for (const QString &val : dataList) {
            bool ok;
            val.toInt(&ok);
            if (!ok) {
                emit errorOccurred("无效的数据格式");
                return QByteArray();
            }
            dataHex += QString("%1").arg(val.toInt(), 4, 16, QChar('0')).toUpper();
        }
        message += (byteCountHex + dataHex);
    }

    QByteArray request = QByteArray::fromHex(message.toUtf8());
    QByteArray crcData = calculateCrc(request);
    request.append(crcData);
    return request;
}

QString ModbusRTU::parseModbusResponse(const QByteArray &response, int expectedCoilCount)
{
    QString log;
    if (response.size() < 5) {
        return "接收到的响应帧太短，无法解析";
    }

    QByteArray dataWithoutCrc = response.left(response.size() - 2);
    QByteArray receivedCrc = response.right(2);
    QByteArray calculatedCrc = calculateCrc(dataWithoutCrc);

    if (receivedCrc != calculatedCrc) {
        return "响应帧 CRC 校验失败\n";
    }

    quint8 slaveAddr = static_cast<quint8>(response[0]);
    quint8 functionCode = static_cast<quint8>(response[1]);

    log += "响应帧: " + response.toHex(' ').toUpper() + "\n";
    log += QString("从站地址: 0x%1\n").arg(slaveAddr, 2, 16, QLatin1Char('0'));
    log += QString("功能码:    0x%1\n").arg(functionCode, 2, 16, QLatin1Char('0'));

    if (functionCode & 0x80) {
        quint8 exceptionCode = static_cast<quint8>(response[2]);
        QString exceptionMsg;
        switch (exceptionCode) {
        case 0x01: exceptionMsg = "非法功能码"; break;
        case 0x02: exceptionMsg = "非法数据地址"; break;
        case 0x03: exceptionMsg = "非法数据值"; break;
        case 0x04: exceptionMsg = "从站设备故障"; break;
        default:   exceptionMsg = "未知异常"; break;
        }
        log += QString("异常响应:异常码 0x%1 - %2\n").arg(exceptionCode, 2, 16, QLatin1Char('0')).arg(exceptionMsg);
        return log;
    }

    switch (functionCode) {
    case 0x01:
    {
        quint8 byteCount = static_cast<quint8>(response[2]);
        log += QString("字节数:   %1\n").arg(byteCount);

        int coilIndex = 0; // 当前线圈编号
        for (int i = 0; i < byteCount && coilIndex < expectedCoilCount; ++i) {
            quint8 byteVal = static_cast<quint8>(response[3 + i]);
            for (int b = 0; b < 8 && coilIndex < expectedCoilCount; ++b) {
                int bitVal = (byteVal >> b) & 0x01;
                log += QString("线圈[%1]:  %2\n").arg(coilIndex).arg(bitVal);
                ++coilIndex;
            }
        }
        break;
    }
    case 0x03: // 读保持寄存器
    {
        quint8 byteCount = static_cast<quint8>(response[2]);
        log += QString("字节数:    %1\n").arg(byteCount);
        for (int i = 0; i < byteCount / 2; ++i) {
            quint16 regVal = static_cast<quint8>(response[3 + 2 * i]) << 8 | static_cast<quint8>(response[4 + 2 * i]);
            log += QString("寄存器[%1]: %2\n").arg(i).arg(regVal);
        }
        break;
    }
    case 0x0F:
    case 0x10: {
        if (response.size() < 6) return "响应数据不足，无法解析写入结果";
        quint16 startAddr = static_cast<quint8>(response[2]) << 8 | static_cast<quint8>(response[3]);
        quint16 quantity = static_cast<quint8>(response[4]) << 8 | static_cast<quint8>(response[5]);
        log += QString("写入起始地址: %1\n").arg(startAddr);
        log += QString("写入数量: %1\n").arg(quantity);
        break;
    }
    default:
        log += QString("未识别的功能码: 0x%1\n").arg(functionCode, 2, 16, QLatin1Char('0')).toUpper();
        break;
    }

    log += "CRC校验通过\n";
    return log;
}

void ModbusRTU::onDataReady(const QByteArray &data)
{
    if (!waitingResponse_) return;

    responseBuffer_ += data;
    if (responseBuffer_.size() < 5) return;

    quint8 funcCode = static_cast<quint8>(responseBuffer_[1]);
    int expectedLen = 0;

    if (funcCode == 0x01 || funcCode == 0x03) {
        if (responseBuffer_.size() < 3) return;
        quint8 byteCount = static_cast<quint8>(responseBuffer_[2]);
        expectedLen = 3 + byteCount + 2;
    } else if (funcCode == 0x0F || funcCode == 0x10) {
        expectedLen = 8;
    } else if (funcCode & 0x80) {
        expectedLen = 5;
    } else {
        return;
    }

    if (responseBuffer_.size() < expectedLen) return;

    timeoutTimer_->stop();
    waitingResponse_ = false;
    currentRetryCount_ = 0;

    QByteArray fullResponse = responseBuffer_.left(expectedLen);
    responseBuffer_.remove(0, expectedLen);

    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
    QString log = QString("[%1] 响应帧解析:\n").arg(timestamp) +
                  parseModbusResponse(fullResponse, expectedCoilCount_);
    emit responseReady(log);
}

void ModbusRTU::startTimeoutMonitoring(int timeout)
{
    timeoutTimer_->start(timeout);
}

void ModbusRTU::handleTimeout()
{
    if (!waitingResponse_) return;

    timeoutTimer_->stop();

    if (currentRetryCount_ >= currentParams_.retryCount) {
        QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
        emit responseReady(QString("[%1] 超过最大重发次数，终止请求\n").arg(timestamp));
        waitingResponse_ = false;
        return;
    }

    currentRetryCount_++;
    serialManager_->writeData(lastRequestFrame_);
    timeoutTimer_->start(currentParams_.timeout);

    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
    emit responseReady(QString("[%1] 超时未响应，重发第 %2 次").arg(timestamp).arg(currentRetryCount_));
}
